#include "async_client.h"
#include "connection_settings.h"

namespace cs
{
	async_client::async_client(callback_t callback):
		callback_(callback)
	{
		boost::asio::ip::tcp::resolver resolver(io_service_);
		boost_asio_tcp::resolver::query query(constant::server_address, constant::server_port);
		boost_asio_tcp::resolver::iterator iterator = resolver.resolve(query);
		
		socket_.reset(new boost_asio_tcp::socket(io_service_));
		boost::asio::async_connect(*socket_.get(), iterator,
				boost::bind(&async_client::handle_connect, this,
				boost::asio::placeholders::error));

	}

	void async_client::send(message& msg)
	{
		io_service_.post(boost::bind(&async_client::do_send, this, msg));
	}

	void async_client::close()
	{
		io_service_.post(boost::bind(&async_client::do_close, this));
	}

	void async_client::handle_connect(const boost::system::error_code& error)
	{
		if (!error) {
			boost::asio::async_read(*socket_.get(),
				boost::asio::buffer(read_msg_->get_header().data(), cs::message::header_length),
				boost::bind(&async_client::handle_read_header, this, boost::asio::placeholders::error));
		}
	}

	void async_client::handle_read_header(const boost::system::error_code& error)
	{
		if (!error && callback_) {

			read_msg_->decode_header();

			boost::asio::async_read(*socket_.get(),
				boost::asio::buffer(read_msg_->get_header().data(), cs::message::header_length),
				boost::bind(&async_client::handle_read_body, this, boost::asio::placeholders::error));
		} else {
			do_close();
		}
	}

	void async_client::handle_read_body(const boost::system::error_code& error)
	{
	}

	void async_client::do_send(const cs::message& msg)
	{
		bool write_in_progress = !write_msgs_.empty();
		write_msgs_.push_back(msg);
		if (!write_in_progress) {
			boost::asio::async_write(*socket_.get(),
				boost::asio::buffer(write_msgs_.front().get_header().data(), message::header_length),
				boost::bind(&async_client::handle_send, this,	boost::asio::placeholders::error));
		}
	}

	void async_client::handle_send(const boost::system::error_code& error)
	{
		if (!error) {
			write_msgs_.pop_front();
			if (!write_msgs_.empty()) {
				boost::asio::async_write(*socket_.get(),
					boost::asio::buffer(write_msgs_.front().get_header().data(), message::header_length),
					boost::bind(&async_client::handle_send, this, boost::asio::placeholders::error));
			}
		} else {
			do_close();
		}
	}

	async_client::~async_client()
	{
		do_close();
	}

	void async_client::do_close()
	{
		if (socket_->is_open())
		{
			socket_->close();
			thread->join();
		}
	}
}